<template>
  <!-- 侧边滑出的图表模态框 -->
  <transition name="slide-modal">
    <div v-if="visible" class="fixed inset-0 z-50 flex">
      <!-- 背景遮罩 -->
      <div class="fixed inset-0 bg-black/50 backdrop-blur-sm" @click="close"></div>
      
      <!-- 滑出面板 -->
      <div class="ml-auto w-full max-w-5xl bg-white shadow-2xl">
        <!-- 顶部工具栏 -->
        <header class="bg-gray-50 border-b border-gray-200 sticky top-0 z-10">
          <div class="px-6 py-4 flex items-center justify-between">
            <div class="flex items-center gap-4">
              <button @click="close" 
                class="flex items-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-all duration-200 font-bold shadow-md">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
                  <path fill-rule="evenodd"
                    d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z"
                    clip-rule="evenodd" />
                </svg>
                关闭
              </button>
              <div class="text-gray-800">
                <h1 class="text-xl font-bold">{{ title }}</h1>
                <p class="text-sm text-gray-600">{{ subtitle }}</p>
              </div>
            </div>
            
            <div class="flex items-center gap-4">
              <!-- 操作按钮 -->
              <button @click="downloadImage" 
                class="px-4 py-2 bg-green-50 text-green-600 border border-green-200 rounded-md hover:bg-green-100 transition-all duration-200 font-bold shadow-sm">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1 inline-block" viewBox="0 0 20 20" fill="currentColor">
                  <path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
                </svg>
                保存图片
              </button>
              
              <button @click="resetView" 
                class="px-4 py-2 bg-blue-50 text-blue-600 border border-blue-200 rounded-md hover:bg-blue-100 transition-all duration-200 font-bold shadow-sm">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1 inline-block" viewBox="0 0 20 20" fill="currentColor">
                  <path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" />
                </svg>
                重置视图
              </button>
            </div>
          </div>
        </header>

        <!-- 图表内容区域 -->
        <main class="flex-1 p-6 h-full overflow-auto bg-gray-50" style="display:flex;flex-direction:column;">
          <div class="h-full flex flex-col flex-1 m-0 p-0" style="height:100%;flex:1;">
            <!-- 图表容器填满整个内容区 -->
            <div class="chart-container-full" ref="chartContainer"></div>
            <!-- 如需说明区可保留，否则建议去除 -->
            <div v-if="description" class="mt-6 bg-white rounded-xl border border-gray-200 shadow-lg p-6">
              <h3 class="text-lg font-bold text-gray-800 mb-3">图表说明</h3>
              <p class="text-gray-600 leading-relaxed">{{ description }}</p>
            </div>
          </div>
        </main>
      </div>
    </div>
  </transition>
</template>

<script>
import * as echarts from 'echarts';

export default {
  name: 'SlideGraphModal',
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      required: true,
      validator: value => ['flowchart', 'mindmap'].includes(value)
    },
    data: {
      type: Object,
      required: true
    },
    policyTitle: {
      type: String,
      default: ''
    }
  },
  
  data() {
    return {
      chart: null,
      loading: true,
      error: null,
      title: '',
      subtitle: '',
      description: '',
      chartOption: null
    };
  },
  
  watch: {
    visible(newVal) {
      if (newVal) {
        this.$nextTick(() => {
          this.initializeGraph();
        });
      } else {
        this.disposeChart();
      }
    },
    
    data: {
      handler() {
        if (this.visible) {
          this.initializeGraph();
        }
      },
      deep: true
    }
  },
  
  mounted() {
    window.addEventListener('resize', this.handleResize);
    // 强制初始化图表，便于调试
    this.initializeGraph();
  },
  
  beforeUnmount() {
    this.disposeChart();
    window.removeEventListener('resize', this.handleResize);
  },
  
  methods: {
    async initializeGraph() {
      try {
        console.log('[调试] SlideGraphModal弹窗收到的数据:', this.type, this.data);
        this.loading = true;
        this.error = null;
        // 兼容传入数据为字符串的情况
        let chartData = this.data;
        if (!chartData || (typeof chartData === 'object' && Object.keys(chartData).length === 0)) {
          this.error = '未收到后端数据或数据为空';
          this.loading = false;
          return;
        }
        if (typeof chartData === 'string') {
          try {
            chartData = JSON.parse(chartData);
          } catch (e) {
            this.error = '图表数据解析失败';
            this.loading = false;
            return;
          }
        }
        if (this.type === 'flowchart') {
          this.loadFlowchart(chartData);
        } else if (this.type === 'mindmap') {
          this.loadMindmap(chartData);
        } else {
          throw new Error('未知的图表类型');
        }
        await this.$nextTick();
        // 强制调用 renderChart，确保日志输出
        this.renderChart();
      } catch (error) {
        console.error('[调试] 图表初始化失败:', error);
        this.error = error.message || '图表加载失败';
      } finally {
        this.loading = false;
      }
    },
    loadFlowchart(chartData) {
      this.title = '政策办理流程图';
      this.subtitle = '按步骤顺序展示的政策执行流程';
      this.description = '此流程图按照时间顺序展示政策办理的完整流程，绿色表示开始步骤，蓝色表示中间流程，红色表示结束步骤。箭头指向下一个步骤，体现流程的先后顺序。';
      if (!chartData || !Array.isArray(chartData.nodes) || !chartData.nodes.length) {
        throw new Error('流程图数据不可用');
      }
      this.chartOption = this.buildFlowchartOption(chartData);
    },
    loadMindmap(chartData) {
      console.log('[调试] loadMindmap:', chartData);
      this.title = '政策结构图';
      this.subtitle = '政策知识结构的思维导图';
      // 仅保留主题、时间、空间维度说明
      let dim = this.$attrs.dimension || '';
      if (dim === 'time') {
        this.description = '时间维度结构图：以时间轴方式展示政策知识结构，突出时间性。';
      } else if (dim === 'space') {
        this.description = '空间维度结构图：以空间分区方式展示政策知识结构，突出空间感。';
      } else if (dim === 'theme') {
        this.description = '主题维度结构图：以主题分支方式展示政策知识结构，突出主题归类。';
      } else {
        this.description = '请选择主题、时间或空间维度进行结构图展示。';
      }
      if (!chartData || !chartData.title) {
        throw new Error('结构图数据不可用');
      }
      this.chartOption = this.buildMindmapOption(chartData, dim);
    },
    buildFlowchartOption(data) {
      const nodes = Array.isArray(data.nodes) ? data.nodes : [];
      const links = Array.isArray(data.links) ? data.links : [];
      
      if (!nodes.length) {
        throw new Error('流程图节点数据为空');
      }
      
      // 计算节点分层分行坐标
      const processedNodes = this.calculateFlowchartLayout(nodes, links);
      return {
        backgroundColor: '#ffffff',
        animation: true,
        animationDuration: 1000,
        animationEasing: 'cubicOut',
        tooltip: {
          trigger: 'item',
          formatter: function(params) {
            if (params.dataType === 'node') {
              const stepNumber = params.data.stepNumber ? `步骤${params.data.stepNumber}: ` : '';
              return `<div style="padding: 12px; max-width: 300px;">
                <div style="font-weight: bold; color: #1f2937; font-size: 14px;">${stepNumber}${params.data.name}</div>
                ${params.data.description ? `<div style="color: #6b7280; margin-top: 4px; font-size: 12px;">${params.data.description}</div>` : ''}
              </div>`;
            }
            return '';
          },
          backgroundColor: 'rgba(255, 255, 255, 0.95)',
          borderColor: '#e5e7eb',
          borderWidth: 1,
          textStyle: {
            color: '#374151'
          }
        },
        series: [{
          type: 'graph',
          layout: 'none', // 关键：手动布局
          roam: true,
          data: processedNodes,
          links: links.map((link, index) => ({
            source: link.source,
            target: link.target,
            symbol: ['none', 'arrow'],
            symbolSize: [6, 12], // 箭头更小
            lineStyle: {
              color: '#16a34a',
              width: 2.5, // 线宽更细
              curveness: 0.08,
              shadowBlur: 3,
              shadowColor: 'rgba(22, 163, 74, 0.18)',
              opacity: 0.85
            },
            label: {
              show: link.label || link.name || link.description ? true : false,
              formatter: link.label || link.name || link.description || '',
              fontSize: 9, // 字体更小
              color: '#15803d',
              backgroundColor: 'rgba(255, 255, 255, 0.95)',
              padding: [2, 5], // 边距更小
              borderRadius: 4,
              borderColor: '#16a34a',
              borderWidth: 1,
              distance: 5,
              position: 'middle',
              fontWeight: 'normal'
            }
          })),
          emphasis: {
            disabled: true
          }
        }]
      };
    },
    
    // 新增方法：计算流程图布局
    calculateFlowchartLayout(nodes, links) {
      // 构建节点Map
      const nodeMap = {};
      nodes.forEach(n => { nodeMap[n.id || n.name] = n; });

      // 始终采用 links 拓扑分层
      const inDegree = {};
      const outNodes = {};
      nodes.forEach(node => {
        const nodeId = node.id || node.name;
        inDegree[nodeId] = 0;
        outNodes[nodeId] = [];
      });
      links.forEach(link => {
        inDegree[link.target]++;
        outNodes[link.source].push(link.target);
      });
      const levels = [];
      const queue = [];
      Object.keys(inDegree).forEach(nodeId => {
        if (inDegree[nodeId] === 0) queue.push(nodeId);
      });
      const nodeLevel = {};
      queue.forEach(id => nodeLevel[id] = 0);
      while (queue.length > 0) {
        const currentLevelSize = queue.length;
        const currentLevel = [];
        for (let i = 0; i < currentLevelSize; i++) {
          const nodeId = queue.shift();
          currentLevel.push(nodeId);
          outNodes[nodeId].forEach(nextNodeId => {
            inDegree[nextNodeId]--;
            if (inDegree[nextNodeId] === 0) {
              queue.push(nextNodeId);
              nodeLevel[nextNodeId] = (nodeLevel[nodeId] || 0) + 1;
            }
          });
        }
        if (currentLevel.length > 0) levels.push(currentLevel);
      }
      // 有环或孤立节点
      const processedNodes = new Set(levels.flat());
      const remainingNodes = nodes.filter(node => !processedNodes.has(node.id || node.name)).map(n => n.id || n.name);
      if (remainingNodes.length > 0) levels.push(remainingNodes);
      const layered = levels;

      // 计算每层坐标
      const result = [];
      const containerWidth = 1600;
      const containerHeight = 900;
      const minNodeSpacing = 120; // 节点间距减小
      const minLevelHeight = 120; // 层间距减小
      const levelHeight = Math.max(containerHeight / layered.length, minLevelHeight);
      layered.forEach((level, levelIndex) => {
        const y = levelIndex * levelHeight + levelHeight / 2 + 40;
        const levelWidth = Math.max((level.length - 1) * minNodeSpacing, 0);
        const startX = (containerWidth - levelWidth) / 2;
        level.forEach((nodeId, nodeIndex) => {
          const originalNode = nodeMap[nodeId];
          if (originalNode) {
            // 样式同前
            let nodeColor = '#3b82f6';
            let nodeShape = 'circle';
            let nodeSize = 100;
            if (originalNode.type === 'policy_issuer') {
              nodeColor = '#22c55e';
              nodeShape = 'roundRect';
              nodeSize = 80;
            } else if (originalNode.type === 'policy_receiver') {
              nodeColor = '#38bdf8';
              nodeShape = 'rect';
              nodeSize = 70;
            } else if (originalNode.type === 'service_category') {
              nodeColor = '#fbbf24';
              nodeShape = 'rect';
              nodeSize = 60;
            } else if (originalNode.type === 'process_step') {
              nodeColor = '#ef4444';
              nodeShape = 'diamond';
              nodeSize = 70;
            } else {
              nodeSize = 60;
            }
            const x = startX + nodeIndex * minNodeSpacing;
            result.push({
              id: nodeId,
              name: originalNode.name || originalNode.label || originalNode.title || `步骤${levelIndex + 1}-${nodeIndex + 1}`,
              x: x,
              y: y,
              stepNumber: levelIndex + 1,
              description: originalNode.description || '',
              symbol: nodeShape,
              symbolSize: [nodeSize, nodeSize * 0.7],
              itemStyle: {
                color: nodeColor,
                borderColor: this.getDarkerColor(nodeColor),
                borderWidth: 6,
                shadowBlur: 24,
                shadowColor: `${nodeColor}99`,
                opacity: 1
              },
              label: {
                show: true,
                color: '#fff',
                fontWeight: 'bold',
                fontSize: 11, // 字体更小
                fontFamily: 'PingFang SC, Microsoft YaHei, Arial, sans-serif',
                backgroundColor: `${nodeColor}`,
                padding: [4, 8],
                borderRadius: 8,
                borderColor: '#fff',
                borderWidth: 2,
                textBorderColor: 'rgba(0,0,0,0.18)',
                textBorderWidth: 1
              }
            });
          }
        });
      });
      return result;
    },
    
    // 获取更深的颜色
    getDarkerColor(color) {
      const colorMap = {
        '#22c55e': '#16a34a',
        '#3b82f6': '#1e40af', 
        '#ef4444': '#dc2626'
      };
      return colorMap[color] || color;
    },
    
    buildMindmapOption(data, dimension = '') {
      const nodes = [];
      const links = [];
      let idx = 0;
      // 维度样式参数
      let layout = 'force';
      let colorFn = (level, node) => this.getMindmapNodeColor(level);
      let edgeColorFn = (level) => this.getMindmapLinkColor(level);
      let labelColorFn = (level) => '#ffffff'; // 默认字体颜色
      let themeBranchColors = ['#ffb300', '#00e676', '#ff4081', '#7c4dff', '#00bcd4', '#ff5252', '#ffd600', '#536dfe', '#69f0ae'];
      if (dimension === 'time') {
        layout = 'circular';
        colorFn = (level, node) => level === 0 ? '#60a5fa' : '#34d399';
        edgeColorFn = () => '#93c5fd';
        labelColorFn = (level) => '#ffffff'; // 统一白色字体
      } else if (dimension === 'space') {
        layout = 'force';
        colorFn = (level, node) => node.regionColor || ['#60a5fa', '#34d399', '#fbbf24', '#a78bfa'][level % 4];
        edgeColorFn = () => '#93c5fd';
        labelColorFn = (level) => '#ffffff';
      } else if (dimension === 'theme') {
        layout = 'force';
        colorFn = (level, node) => themeBranchColors[node.themeIdx % themeBranchColors.length] || '#60a5fa';
        edgeColorFn = () => '#fecaca';
        labelColorFn = (level) => '#ffffff';
      }
      const traverse = (node, parentId = null, level = 0, branchIdx = 0) => {
        const id = 'n' + (idx++);
        const nodeSize = Math.max(100 - level * 20, 50); // 减小节点大小：从140-25到100-20，最小从80到50
        let themeIdx = branchIdx;
        nodes.push({
          id,
          name: node.title || '未命名节点',
          symbolSize: nodeSize,
          category: level,
          itemStyle: {
            color: colorFn(level, { ...node, themeIdx }),
            borderColor: '#ffffff',
            borderWidth: 3, // 稍微减小边框：从4到3
            shadowBlur: 15, // 减小阴影：从20到15
            shadowColor: colorFn(level, { ...node, themeIdx }),
            opacity: 1
          },
          label: {
            show: true,
            color: labelColorFn(level),
            fontWeight: 'bold',
            fontSize: Math.max(12 - level * 1.5, 9), // 减小字体大小：从16-2到12-1.5，最小从12到9
            fontFamily: 'PingFang SC, Microsoft YaHei, Arial, sans-serif',
            textBorderColor: 'rgba(0, 0, 0, 0.6)',
            textBorderWidth: 2,
            backgroundColor: colorFn(level, { ...node, themeIdx }),
            padding: [6, 8], // 减小内边距：从[8,12]到[6,8]
            borderRadius: 6, // 减小圆角：从8到6
            borderColor: '#ffffff',
            borderWidth: 2,
            shadowBlur: 8, // 减小阴影：从10到8
            shadowColor: colorFn(level, { ...node, themeIdx })
          }
        });
        if (parentId) {
          links.push({
            source: parentId,
            target: id,
            lineStyle: {
              color: edgeColorFn(level),
              width: Math.max(4 - level * 0.6, 2), // 减小连线宽度：从6-0.8到4-0.6，最小从4到2
              curveness: 0.3,
              shadowBlur: 6, // 减小连线阴影：从8到6
              shadowColor: edgeColorFn(level),
              opacity: 1
            }
          });
        }
        if (Array.isArray(node.children)) {
          node.children.forEach((child, idx) => {
            if (typeof child === 'object' && child.title) traverse(child, id, level + 1, dimension === 'theme' && level === 1 ? idx : branchIdx);
          });
        }
      };
      traverse(data);
      return {
        backgroundColor: '#f0f0f0',
        animation: true,
        animationDuration: 1500,
        animationEasing: 'cubicOut',
        series: [{
          type: 'graph',
          layout,
          roam: true,
          draggable: false,
          data: nodes,
          links: links,
          force: {
            repulsion: 600,
            edgeLength: 120,
            gravity: 0.1,
            layoutAnimation: true,
            friction: 0.8
          },
          emphasis: {
            disabled: true
          }
        }]
      };
    },
    
    renderChart() {
      console.log('[调试] renderChart: chartOption=', this.chartOption, 'chartContainer=', this.$refs.chartContainer);
      if (!this.chartOption || !this.$refs.chartContainer) {
        console.warn('[调试] renderChart: chartOption 或 chartContainer 不存在');
        return;
      }
      try {
        this.disposeChart();
        
        this.chart = echarts.init(this.$refs.chartContainer, null, {
          renderer: 'canvas',
          useDirtyRect: false
        });
        
        this.chart.on('finished', () => {
          this.loading = false;
          this.chart.resize();
        });
        
        this.chart.setOption(this.chartOption, true);
        
        setTimeout(() => {
          if (this.chart && !this.chart.isDisposed()) {
            this.chart.resize();
          }
        }, 300);
        
      } catch (error) {
        console.error('[调试] 图表渲染失败:', error);
        this.error = '图表渲染失败';
        this.loading = false;
      }
    },
    
    disposeChart() {
      if (this.chart && !this.chart.isDisposed()) {
        try {
          this.chart.dispose();
        } catch (error) {
          console.warn('图表销毁时出现警告:', error);
        } finally {
          this.chart = null;
        }
      }
    },
    
    handleResize() {
      if (this.chart && !this.chart.isDisposed()) {
        this.$nextTick(() => {
          this.chart.resize();
        });
      }
    },
    
    close() {
      this.$emit('close');
    },
    
    resetView() {
      if (this.chart && !this.chart.isDisposed()) {
        this.chart.dispatchAction({
          type: 'restore'
        });
      }
    },
    
    retryLoad() {
      this.initializeGraph();
    },
    
    downloadImage() {
      if (!this.chart || this.chart.isDisposed()) {
        alert('图表未初始化，无法保存图片');
        return;
      }
      
      try {
        const url = this.chart.getDataURL({
          type: 'png',
          pixelRatio: 2,
          backgroundColor: '#ffffff'
        });
        
        const a = document.createElement('a');
        a.href = url;
        a.download = `${this.title}_${new Date().getTime()}.png`;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        
      } catch (error) {
        console.error('图片保存失败:', error);
        alert('图片保存失败，请稍后重试');
      }
    },
    
    getMindmapNodeColor(level) {
      // 柔和渐变色，提升美观度
      const colors = [
        'linear-gradient(135deg, #60a5fa 0%, #38bdf8 100%)', // 根节点：蓝青渐变
        'linear-gradient(135deg, #34d399 0%, #a7f3d0 100%)', // 一级节点：青绿渐变
        'linear-gradient(135deg, #fbbf24 0%, #fde68a 100%)', // 二级节点：黄橙渐变
        'linear-gradient(135deg, #a78bfa 0%, #f3e8ff 100%)', // 三级节点：紫白渐变
        'linear-gradient(135deg, #fca5a5 0%, #fef2f2 100%)'  // 四级节点：粉白渐变
      ];
      // echarts 不支持 css gradient，取渐变中间色
      const flatColors = [
        '#60a5fa', // 蓝
        '#34d399', // 青绿
        '#fbbf24', // 黄
        '#a78bfa', // 紫
        '#fca5a5'  // 粉
      ];
      return flatColors[level] || flatColors[flatColors.length - 1];
    },
    getMindmapLinkColor(level) {
      // 柔和浅色系
      const colors = [
        '#93c5fd', // 浅蓝
        '#6ee7b7', // 浅青绿
        '#fde68a', // 浅黄
        '#ddd6fe', // 浅紫
        '#fecaca'  // 浅粉
      ];
      return colors[level] || colors[colors.length - 1];
    }
  }
};
</script>

<style scoped>
/* 优化大图展示区域，适配更大画布和更大节点 */
.chart-container-full {
  width: 100vw;
  height: 90vh;
  min-width: 1200px;
  min-height: 700px;
  flex: 1;
  background: #fff;
  border: none;
  box-shadow: none;
  display: flex;
  align-items: stretch;
  justify-content: stretch;
  position: relative;
  overflow: auto;
  margin: 0;
  padding: 0;
}
/* 优化弹窗脑图区域样式，去除顶部空隙，确保echarts图表垂直填满弹窗 */
.chart-container {
  width: 100%;
  height: 100%;
  flex: 1;
  background: #f0f0f0;
  border: 2px solid #ef4444;
  display: flex;
  align-items: stretch;
  justify-content: stretch;
  position: relative;
  overflow-y: auto;
  overflow-x: hidden;
  margin: 0;
  padding: 0;
}
/* 侧边滑出动画 */
.slide-modal-enter-active,
.slide-modal-leave-active {
  transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.slide-modal-enter-active > div:last-child,
.slide-modal-leave-active > div:last-child {
  transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.slide-modal-enter-from,
.slide-modal-leave-to {
  opacity: 0;
}

.slide-modal-enter-from > div:last-child,
.slide-modal-leave-to > div:last-child {
  transform: translateX(100%);
}

/* 加载动画 */
.loading-spinner {
  display: inline-block;
  width: 40px;
  height: 40px;
  border: 4px solid rgba(59, 130, 246, 0.2);
  border-radius: 50%;
  border-top-color: #3b82f6;
  animation: spin 1s ease-in-out infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* 按钮悬停效果 */
button {
  transition: all 0.2s ease-in-out;
}

button:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
</style>
